查看原文
其他

郝健:Linux下服务程序启动管理方式的分析与总结

郝健 Linux阅码场 2021-01-31



作者:郝健

目前就职于瑞星咖啡,负责4层负载均衡的研究与开发。曾就职于天融信,赛尔网络,云杉网络几家公司。主要感兴趣的方向:linux内核网络子系统,dpdk。


目前,Linux平台下主流的服务程序启动管理方式有以下几种: 

  1. daemon

  2.  sysvinit

  3. systemd

  4. nohup


1. daemon 



守护进程是在后台运行不受控端控制的进程,通常情况下守护进程在系统启动时自动运行。 

1.1 概念说明 

进程组 

每个进程除了有一进程ID( PID )之外,还属于一个进程组。进程组是一个或多个进程的集合。每个进程组有一个唯一的进程组ID( PGID )。进程组ID类似于进程ID, 它是一个正整数,并可存放在pid_t数据类型中。函数getpgrp返回调用进程的进程组ID。

会话期(session) 

会话期是一个或多个进程组的集合。通常是由shell的管道将几个进程编成一组的。比如,下图中的安排 可能是由下列形式的shell命令形成的: 

procl | proc2 & 

proc3 | proc4 | proc5

一般,登陆一个shell时就会创建一个会话期。进程调用setsid函数就可建立一个新会话期。

如果调用此函数的进程不是一个进程组的组长,则此函数创建一个新会话期,结果为:

  1. 此进程变成该新会话期的会话期首进程(session leader,会话期首进程是创建该会话期的进 程)。此进程是该新会话期中的唯一进程。

  2. 此进程成为一个新进程组的组长进程。新进程组ID是此调用进程的进程ID。

  3. 此进程没有控制终端。如果在调用setsid之前此进程有一个控制终端,那么这种联系也被解除。如果此调用进程已经是一个进程组的组长,则此函数返回出错。为了保证不处于这种情况,通常先 调用fork,然后使其父进程终止,而子进程则继续。因为子进程继承了父进程的进程组ID,而其进程ID则是新分配的,两者不可能相等,所以这就保证了子进程不是一个进程组的组长。


控制终端(terminal) 

会话期和进程组有一些其他特性:

  1. 一个会话期可以有一个单独的控制终端(controlling terminal)。这通常是我们在其上登录的终端设备(终端登录情况)或伪终端设备(网络登录情况)。

  2. 建立与控制终端连接的会话期首进程,被称为控制进程(controlling process)。

  3.  一个会话期中的几个进程组可被分成一个前台进程组(foreground process group)以及一个或 几个后台进程组(background process group)。

  4.  如果一个会话期有一个控制终端,则它有一个前台进程组,其他进程组则为后台进程组。

  5. 无论何时键入中断键(常常是DELETE或Ctrl -C)或退出键(常常是Ctrl -\),就会造成将中断信号或退出信号送至前台进程组的所有进程。

  6. 如果终端界面检测到网络已经断开连接,则将挂断信号送至控制进程(会话期首进程)。通常,我们不必关心控制终端,登录时,将自动建立控制终端。


1.2 创建方法 

创建守护进程,应该创建一个进程然后将其放到一个新的会话期中。

  1. 首先要做的是调用umask将文件模式创建屏蔽字设置为0。

  2. 调用fork(子进程会是将来的守护进程),然后使父进程退出(exit),保证子进程不是进程组组长(这 是setsid调用的必要前提条件)。

  3. 调用setsid创建新的会话期。

  4. 将当前目录改为根目录(如果不改为根目录可能导致不能umount某个目录)。

  5. 关闭不再需要的文件描述符。

  6. 某些守护进程打开/dev/null使其具有描述符0、1和2(将标准输入、标准输出、标准错误重定向 到/dev/null)。

或者直接调用daemon函数


2. sysvinit 


sysvinit主要依赖于Shell脚本,在/etc/rc.d/rc*.d建立软连接。在RedHat系列发行版中,还提供了 service,chkconfig 等命令行工具,来方便来管理 init 系统。sysvinit的主要问题是启动慢,已经逐渐 被淘汰。

目前Ubuntu和Centos等主流发行版都以移步到systemd,但仍然兼容老的写法,这里简单举个的例 子,编写一个simple脚本,添加执行权限后,保存到/etc/init.d/目录下。

测试:

3. systemd 


systemd是目前主流Linux发型版采用的init项目,systemd起初饱受争议,可以搜索“the software that currently breaks your audio”查看。但随着越来越稳定和使用c开发,高效启动,已经逐渐成为主角。Linus也表示:Torvalds says he has no strong opinions on systemd

https://www.itwire.com/business-it-news/open-source/65402-torvalds-says-he-has-no-strong-opinions-on-systemd

我们简单实现一个simple-server来感受一下systemd,主要步骤如下:

 

1. 编写一个需要作为守护的程序,如:simple-server.c

2. 编写service文件

具体的说明详见;

https://www.freedesktop.org/software/systemd/man/systemd.service.html 

其中Restart=always代表在任何情况下service被杀死后,都需要自动重启,其他说明详见如上链接中的 Table 2。

 

3. sudo cp simple-server.service /lib/systemd/system 

4. sudo systemctl enable simple-server 设置为开机启动,会自动创建软连接 /etc/systemd/system/multi-user.target.wants/simpleserver.service -> /lib/systemd/system/simple-server.service 

5. sudo systemctl start simple-server 启动 

6. sudo systemctl status simple-server 查看状态 

7. cat /var/log/syslog 查看日志输出 

8. pstree | grep simple-server 

9. sudo killall simple-server 杀死服务,验证是否自动重启。


几点说明:

  1. 修改simple-server.service后,需要执行systemctl daemon-reload重新加载。

  2. 在自daemon的方式中,是无法决定服务程序信息输出的位置的,只能重定向到/dev/null,而 systemd的方式可以灵活配置重定向的位置,/var/log/syslog为默认日志路径。

  3. service文件中Restart选项体现了"子死父知因"的重要性。 


4. nohup 


一个terminal一旦关闭,其绑定的shell会收到SIGHUP信号,且shell在退出之前会将SIGHUP信号广播 给其上的进程组,即其上所有进程都会退出。这也是自daemon需要调用setsid脱离terminal的原因。nohup命令并不是真正脱离terminal,而是忽略SIGHUP。将标准输入重定向到/dev/null,标准输出和标准出错重定向到nohup.out,且不会随着terminal的关闭而退出。验证:


1)正常启动一个死循环程序

2)nohup启动同样的死循环程序

对比总结 


  1. daemon 通过自实现daemon,可以清楚的看出一个守护进程实现的原理,方便理清概念。但缺点是在实际工程中默认将输出重定向/dev/null,无法保存服务日志,排查服务问题,即使在程序中重定向到 某个日志目录,也只能写死,修改位置需要重新编译,不够灵活。

  2. sysvinit sysvinit的优点是简单,只需要编写脚本。将服务添加到某个runlevel时,只需要创建软链接文件 即可,DevOPs兴起之前,传统运维人员可以很快上手。顺序执行,方便排查问题。但顺序执行既 是优点也是缺点,带来的弊端是运行效率慢,这也是逐渐被淘汰的主要原因。毕竟现在Linux不只 用于服务器系统,终端用户更追求fast boot的用户体验。

  3. systemd systemd是目前主流Linux发行版中最新的初始化系统,主要的设计目标就是提高系统的启动速 度。.service文件中包含很多选项,配置灵活,比如日志位置和重启方式等。当然,最主要的卖点 还是并行启动速度快。

  4. nohup 在实际工程中,nohup也挺常用。比如一个类服务程序的调试,简单执行即可在一段时间通过 nohup.out文件观察问题,但其并不能算正真的守护程序。


综上,systemd看起来可以满足一个产品级别服务程序的两个最重要的需求:根据不同情况重启;记录 日志灵活可查,而这是daemon和nohup方式无法快速方便满足的。sysvinit简单清晰,但启动慢,就作为传统运维人员的美好记忆保留吧。



Linux阅码场原创精华文章汇总

更多精彩,尽在"Linux阅码场",扫描下方二维码关注

点一点右下角”在看”,为阅码场打Call~

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存